package com.h2.ref.server.web.user.openid;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.openid4java.consumer.ConsumerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.openid.OpenID4JavaConsumer;
import org.springframework.security.openid.OpenIDAuthenticationFilter;
import org.springframework.security.openid.OpenIDAuthenticationToken;
import org.springframework.security.openid.OpenIDConsumer;
import org.springframework.security.openid.OpenIDConsumerException;

public class OpenIdProvider {
   
   private static final Logger LOG = LoggerFactory.getLogger(
         OpenIdProvider.class);
   
   ////
   
   private Map<String, String> _realmMapping = Collections.emptyMap();
   
   private OpenIDConsumer _consumer;
   
   {
      try {
         setConsumer(new OpenID4JavaConsumer());
      } catch (ConsumerException exp) {
         throw new RuntimeException(exp);
      }
   }
   
   ////
   ////
   
   public String obtainIdentity(HttpServletRequest request,
         String claimedIdentity, String returnToUrl) throws OpenIDConsumerException {
      String realm = lookupRealm(returnToUrl);
      String openIdUrl = getConsumer().beginConsumption(request,
            claimedIdentity, returnToUrl, realm);
      if (LOG.isDebugEnabled()) {
         LOG.trace("return_to is '" + returnToUrl + "', realm is '"
               + realm + "'");
         LOG.trace("Redirecting to " + openIdUrl);
      }
      return openIdUrl;
   }

   public OpenIDAuthenticationToken readIdentity(HttpServletRequest request)
         throws OpenIDConsumerException {
      OpenIDAuthenticationToken token = getConsumer().endConsumption(request);
      return token;
   }
   
   protected String lookupRealm(String returnToUrl) {
      String mapping = _realmMapping.get(returnToUrl);
      
      if (mapping == null) {
         try {
            URL url = new URL(returnToUrl);
            int port = url.getPort();
            
            StringBuilder realmBuffer = new StringBuilder(returnToUrl.length())
               .append(url.getProtocol())
               .append("://")
               .append(url.getHost());
            if (port > 0) {
               realmBuffer.append(":").append(port);
            }
            realmBuffer.append("/");
            mapping = realmBuffer.toString();
         } catch (MalformedURLException exp) {
            LOG.warn("returnToUrl was not a valid URL: [" + returnToUrl + "]");
            LOG.debug("returnToUrl was not a valid URL: [" + returnToUrl + "]",
                  exp);
         }
      }
      
      return mapping;
   }
   
   /**
    * Builds the <tt>return_to</tt> URL that will be sent to the OpenID service
    * provider. By default returns the URL of the current request.
    * 
    * @param request the current request which is being processed by this filter
    * @return The <tt>return_to</tt> URL.
    */
   public String buildReturnToUrl(HttpServletRequest request) {
      return request.getRequestURL().toString();
   }
   
   /**
    * Reads the <tt>claimedIdentityFieldName</tt> from the submitted request.
    */
   public String obtainUsername(HttpServletRequest req) {
      return req.getParameter(
            OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD);
   }

   /**
    * @return the realmMapping
    */
   public Map<String, String> getRealmMapping() {
      return _realmMapping;
   }

   /**
    * @param realmMapping the realmMapping to set
    */
   public void setRealmMapping(Map<String, String> realmMapping) {
      _realmMapping = realmMapping;
   }

   /**
    * @return the consumer
    */
   public OpenIDConsumer getConsumer() {
      return _consumer;
   }

   /**
    * @param consumer the consumer to set
    */
   public void setConsumer(OpenIDConsumer consumer) {
      _consumer = consumer;
   }

}
